home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / src / out-of-phase-102-c / OutOfPhase 1.02 Source / OutOfPhase Folder / Level 0 Macintosh 29Sep94 / Menus.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-23  |  15.4 KB  |  508 lines  |  [TEXT/KAHL]

  1. /* Menus.c */
  2. /*****************************************************************************/
  3. /*                                                                           */
  4. /*    System Dependency Library for Building Portable Software               */
  5. /*    Macintosh Version                                                      */
  6. /*    Written by Thomas R. Lawrence, 1993 - 1994.                            */
  7. /*                                                                           */
  8. /*    This file is Public Domain; it may be used for any purpose whatsoever  */
  9. /*    without restriction.                                                   */
  10. /*                                                                           */
  11. /*    This package is distributed in the hope that it will be useful,        */
  12. /*    but WITHOUT ANY WARRANTY; without even the implied warranty of         */
  13. /*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                   */
  14. /*                                                                           */
  15. /*    Thomas R. Lawrence can be reached at tomlaw@world.std.com.             */
  16. /*                                                                           */
  17. /*****************************************************************************/
  18.  
  19. #include "MiscInfo.h"
  20. #include "Debug.h"
  21. #include "Audit.h"
  22. #include "Definitions.h"
  23.  
  24. #ifdef THINK_C
  25.     #pragma options(pack_enums)
  26. #endif
  27. #include <Menus.h>
  28. #include <Fonts.h>
  29. #include <Desk.h>
  30. #ifdef THINK_C
  31.     #pragma options(!pack_enums)
  32. #endif
  33.  
  34. #include "Menus.h"
  35. #include "Memory.h"
  36. #include "Array.h"
  37.  
  38.  
  39. /* structure represents a menu */
  40. struct MenuType
  41.     {
  42.         /* menu resource ID number (must be unique) */
  43.         short                    MenuID;
  44.         /* handle pointing to system's idea of what the menu is */
  45.         MenuHandle        DaMenuHandle;
  46.     };
  47.  
  48.  
  49. /* structure represents a menu item (it remembers the menu and index of item) */
  50. struct MenuItemType
  51.     {
  52.         /* Menu ID + Item Index number used by menu manager to identify item */
  53.         long                    MenuManagerID;
  54.         /* pointer to the menu record for the menu that contains the item. */
  55.         MenuType*            WhatMenu;
  56.     };
  57.  
  58.  
  59. /* list of menus (for finding unique menu IDs) */
  60. static ArrayRec*                    OurMenuList = NIL;
  61.  
  62. /* list of items in the menus (for searching during menu-choice events) */
  63. static ArrayRec*                    OurItemList = NIL;
  64.  
  65. /* the Apple menu record.  NIL means there is no Apple menu */
  66. static MenuType*                    AppleMenu = NIL;
  67.  
  68. /* number of "real" items on the apple menu.  This is set to 0 whenever the */
  69. /* apple menu is created. */
  70. static long                                NumAppleMenuItems;
  71.  
  72. /* flag indicating that menu bar needs to be redrawn */
  73. static MyBoolean                    RedrawMenuBarFlag = False;
  74.  
  75. /* debugging flag */
  76. EXECUTE(static MyBoolean    Initialized = False;)
  77.  
  78.  
  79. /* Initialize the menu subsystem.  This must be called before any menu routines */
  80. /* are used.  It is local to Level 0 and called from module Screen (InitializeScreen) */
  81. /* and should not be called from anywhere else. */
  82. MyBoolean                            Eep_InitializeMenus(void)
  83.     {
  84.         ERROR(Initialized,PRERR(ForceAbort,"InitializeMenus called more than once"));
  85.         /* create list that contains menus */
  86.         OurMenuList = NewArray();
  87.         if (OurMenuList == NIL)
  88.             {
  89.              FailurePoint1:
  90.                 return False;
  91.             }
  92.         /* create the list that will hold all of the menu item records (for searching) */
  93.         OurItemList = NewArray();
  94.         if (OurItemList == NIL)
  95.             {
  96.              FailurePoint2:
  97.                 DisposeArray(OurMenuList);
  98.                 goto FailurePoint1;
  99.             }
  100.         /* initialize the apple menu variable to indicate that there isn't one */
  101.         AppleMenu = NIL;
  102.         EXECUTE(Initialized = True);
  103.         return True;
  104.     }
  105.  
  106.  
  107. /* Destroy any menu stuff that needs to be cleaned up before the program quits. */
  108. /* this should not be called from anywhere else except ShutdownScreen */
  109. void                                    Eep_ShutdownMenus(void)
  110.     {
  111.         ERROR(!Initialized,PRERR(ForceAbort,
  112.             "ShutdownMenus:  Menu manager hasn't been initialized"));
  113.         ERROR(ArrayGetLength(OurMenuList) != 0,PRERR(AllowResume,
  114.             "Eep_ShutdownMenus:  still some menus in existence"));
  115.         ERROR(ArrayGetLength(OurItemList) != 0,PRERR(AllowResume,
  116.             "Eep_ShutdownMenus:  still some menu items in existence"));
  117.         DisposeArray(OurMenuList);
  118.         DisposeArray(OurItemList);
  119.     }
  120.  
  121.  
  122. /* create an implementation defined "utility" menu.  On the Macintosh, this is */
  123. /* the standard "Apple Menu". */
  124. MenuType*                            MakeAppleMenu(void)
  125.     {
  126.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  127.         /* if an apple menu exists, we return it instead of creating a new one */
  128.         if (AppleMenu == NIL)
  129.             {
  130.                 /* create a new menu normally with an apple character */
  131.                 AppleMenu = MakeNewMenu("\024");
  132.                 if (AppleMenu == NIL)
  133.                     {
  134.                         return NIL;
  135.                     }
  136.                 /* initialize item count */
  137.                 NumAppleMenuItems = 0;
  138.                 /* append the line and desk accessory list */
  139.                 AppendMenu(AppleMenu->DaMenuHandle,"\p(-");
  140.                 AddResMenu(AppleMenu->DaMenuHandle,'DRVR');
  141.             }
  142.         CheckPtrExistence(AppleMenu);
  143.         return AppleMenu;
  144.     }
  145.  
  146.  
  147. /* create a new menu with the specified name.  The menu will not */
  148. /* be displayed on the menu bar */
  149. MenuType*                            MakeNewMenu(char* MenuName)
  150.     {
  151.         unsigned char                NameTemp[256];
  152.         long                                Scan;
  153.         long                                Limit;
  154.         MenuType*                        Menu;
  155.  
  156.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  157.         /* allocate the menu record */
  158.         Menu = (MenuType*)AllocPtrCanFail(sizeof(MenuType),"MenuType");
  159.         if (Menu == NIL)
  160.             {
  161.              FailurePoint1:
  162.                 return NIL;
  163.             }
  164.         /* convert name to a pascal string */
  165.         Scan = 0;
  166.         while ((Scan < 255) && (MenuName[Scan] != 0))
  167.             {
  168.                 NameTemp[Scan + 1] = MenuName[Scan];
  169.                 Scan += 1;
  170.             }
  171.         NameTemp[0] = Scan;
  172.         /* find an unused ID number */
  173.         Limit = ArrayGetLength(OurMenuList);
  174.         Menu->MenuID = 256; /* skip 0..255 which are reserved for hierarchical menus */
  175.      LoopPoint:
  176.         for (Scan = 0; Scan < Limit; Scan += 1)
  177.             {
  178.                 if (Menu->MenuID == ((MenuType*)ArrayGetElement(OurMenuList,Scan))->MenuID)
  179.                     {
  180.                         Menu->MenuID += 1;
  181.                         goto LoopPoint;
  182.                     }
  183.                 ERROR(Scan > 16383,PRERR(ForceAbort,"MakeNewMenu:  out of menu IDs"));
  184.             }
  185.         /* allocate the menu itself */
  186.         Menu->DaMenuHandle = NewMenu(Menu->MenuID,NameTemp);
  187.         if (Menu->DaMenuHandle == NIL)
  188.             {
  189.              FailurePoint2:
  190.                 ReleasePtr((char*)Menu);
  191.                 goto FailurePoint1;
  192.             }
  193.         /* add menu to list */
  194.         if (!ArrayAppendElement(OurMenuList,Menu))
  195.             {
  196.              FailurePoint3:
  197.                 DisposeMenu(Menu->DaMenuHandle);
  198.                 goto FailurePoint2;
  199.             }
  200.         return Menu;
  201.     }
  202.  
  203.  
  204. /* hide a menu if it's on the menu bar and delete it and all of the items */
  205. /* it contains. */
  206. void                                    KillMenuAndDeleteItems(MenuType* TheMenu)
  207.     {
  208.         long                                Scan;
  209.         long                                Limit;
  210.  
  211.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  212.         CheckPtrExistence(TheMenu);
  213.         /* remove menu from our list of menus */
  214.         ArrayDeleteElement(OurMenuList,ArrayFindElement(OurMenuList,TheMenu));
  215.         /* remove menu from the menu bar */
  216.         HideMenu(TheMenu);
  217.         /* dispose the system menu block */
  218.         DisposeMenu(TheMenu->DaMenuHandle);
  219.         /* reset the apple menu value if we're disposing that */
  220.         if (TheMenu == AppleMenu)
  221.             {
  222.                 AppleMenu = NIL;
  223.             }
  224.         /* dispose all translation entries for the menu */
  225.         Limit = ArrayGetLength(OurItemList);
  226.         Scan = 0;
  227.         while (Scan < Limit)
  228.             {
  229.                 MenuItemType*            MenuItem;
  230.  
  231.                 MenuItem = (MenuItemType*)ArrayGetElement(OurItemList,Scan);
  232.                 if ((MenuItem->MenuManagerID >> 16) == TheMenu->MenuID)
  233.                     {
  234.                         /* dispose the item */
  235.                         ArrayDeleteElement(OurItemList,Scan);
  236.                         ReleasePtr((char*)MenuItem);
  237.                         /* since there's one less item now, decrement limit */
  238.                         Limit -= 1;
  239.                         /* note that we don't increment scan */
  240.                     }
  241.                  else
  242.                     {
  243.                         /* scan is only incremented if we didn't delete the element */
  244.                         Scan += 1;
  245.                     }
  246.             }
  247.         /* finally, delete the menu record */
  248.         ReleasePtr((char*)TheMenu);
  249.     }
  250.  
  251.  
  252. /* post a menu to the menu bar if it isn't already there */
  253. void                                    ShowMenu(MenuType* TheMenu)
  254.     {
  255.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  256.         CheckPtrExistence(TheMenu);
  257.         /* append menu to menu bar */
  258.         InsertMenu(TheMenu->DaMenuHandle,0);
  259.         /* set flag to indicate that the menu bar needs to be redrawn */
  260.         RedrawMenuBarFlag = True;
  261.     }
  262.  
  263.  
  264. /* remove a menu from the menu bar if it is there */
  265. void                                    HideMenu(MenuType* TheMenu)
  266.     {
  267.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  268.         CheckPtrExistence(TheMenu);
  269.         /* remove menu from menu bar */
  270.         DeleteMenu(TheMenu->MenuID);
  271.         /* set flag to indicate that the menu bar needs to be redrawn */
  272.         RedrawMenuBarFlag = True;
  273.     }
  274.  
  275.  
  276. /* append a new item to an existing menu.  The Shortcut specifies a key that */
  277. /* can be used instead of pulling down the menu.  How this is done and which */
  278. /* keys are allowed are implementation defined.  On the Macintosh, the Command */
  279. /* key is used; keys should be numbers or upper case letters.  If two menu items */
  280. /* are specified with the same shortcut, the result is undefined. */
  281. /* by default, the item is greyed out (disabled). */
  282. MenuItemType*                    MakeNewMenuItem(MenuType* TheMenu, char* MenuItemName,
  283.                                                 char Shortcut)
  284.     {
  285.         unsigned char                NameTemp[256];
  286.         long                                Scan;
  287.         MenuItemType*                MenuItem;
  288.         short                                Index;
  289.  
  290.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  291.         CheckPtrExistence(TheMenu);
  292.         /* allocate record */
  293.         MenuItem = (MenuItemType*)AllocPtrCanFail(sizeof(MenuItemType),"MenuItemType");
  294.         if (MenuItem == NIL)
  295.             {
  296.              FailurePoint1:
  297.                 return NIL;
  298.             }
  299.         /* add item to the item list */
  300.         if (!ArrayAppendElement(OurItemList,MenuItem))
  301.             {
  302.              FailurePoint2:
  303.                 ReleasePtr((char*)MenuItem);
  304.                 goto FailurePoint1;
  305.             }
  306.         /* find out where on menu the item should be placed */
  307.         if (TheMenu == AppleMenu)
  308.             {
  309.                 Index = NumAppleMenuItems + 1;
  310.                 NumAppleMenuItems += 1;
  311.             }
  312.          else
  313.             {
  314.                 Index = CountMItems(TheMenu->DaMenuHandle) + 1;
  315.             }
  316.         /* insert a dummy item (name will be changed) */
  317.         InsMenuItem(TheMenu->DaMenuHandle,"\px",Index - 1);
  318.         /* convert name */
  319.         Scan = 0;
  320.         while ((Scan < 255) && (MenuItemName[Scan] != 0))
  321.             {
  322.                 NameTemp[Scan + 1] = MenuItemName[Scan];
  323.                 Scan += 1;
  324.             }
  325.         NameTemp[0] = Scan;
  326.         /* set the name properly */
  327.         SetItem(TheMenu->DaMenuHandle,Index,NameTemp);
  328.         SetItemCmd(TheMenu->DaMenuHandle,Index,Shortcut);
  329.         /* set item's ID mapping */
  330.         MenuItem->MenuManagerID = ((long)(TheMenu->MenuID) << 16) | Index;
  331.         MenuItem->WhatMenu = TheMenu;
  332.         return MenuItem;
  333.     }
  334.  
  335.  
  336. /* delete the specified item from the menu. */
  337. void                                    KillMenuItem(MenuItemType* TheItem)
  338.     {
  339.         long                            Scan;
  340.         long                            Limit;
  341.  
  342.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  343.         CheckPtrExistence(TheItem);
  344.         /* remove item from item list */
  345.         ArrayDeleteElement(OurItemList,ArrayFindElement(OurItemList,TheItem));
  346.         /* delete item from actual menu */
  347.         DelMenuItem(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff);
  348.         /* fix up item IDs for items after this one on the same menu */
  349.         Limit = ArrayGetLength(OurItemList);
  350.         for (Scan = 0; Scan < Limit; Scan += 1)
  351.             {
  352.                 MenuItemType*            OtherItem;
  353.  
  354.                 OtherItem = (MenuItemType*)ArrayGetElement(OurItemList,Scan);
  355.                 if (((TheItem->MenuManagerID >> 16) == (OtherItem->MenuManagerID >> 16))
  356.                     && ((TheItem->MenuManagerID & 0xffff) < (OtherItem->MenuManagerID & 0xffff)))
  357.                     {
  358.                         /* all items after this one move up.  We don't have to do masking and */
  359.                         /* all that because item IDs (low 16 bits) are positive, so a borrow */
  360.                         /* will never occur */
  361.                         OtherItem->MenuManagerID -= 1;
  362.                     }
  363.             }
  364.         /* fix up apple menu length */
  365.         if (TheItem->WhatMenu == AppleMenu)
  366.             {
  367.                 NumAppleMenuItems -= 1;
  368.             }
  369.         /* release the memory */
  370.         ReleasePtr((char*)TheItem);
  371.     }
  372.  
  373.  
  374. /* enable a menu item. Items may only be selected if enabled. */
  375. void                                    EnableMenuItem(MenuItemType* TheItem)
  376.     {
  377.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  378.         CheckPtrExistence(TheItem);
  379.         EnableItem(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff);
  380.     }
  381.  
  382.  
  383. /* disable a menu item. */
  384. void                                    DisableMenuItem(MenuItemType* TheItem)
  385.     {
  386.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  387.         CheckPtrExistence(TheItem);
  388.         DisableItem(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff);
  389.     }
  390.  
  391.  
  392. /* Set an implementation defined mark to indicate that the menu item has been */
  393. /* persistently selected.  On the Macintosh, this places a checkmark to the left */
  394. /* of the name of the menu item */
  395. void                                    SetItemCheckmark(MenuItemType* TheItem)
  396.     {
  397.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  398.         CheckPtrExistence(TheItem);
  399.         SetItemMark(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff,checkMark);
  400.     }
  401.  
  402.  
  403. /* remove the implementation defined mark */
  404. void                                    ClearItemCheckmark(MenuItemType* TheItem)
  405.     {
  406.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  407.         CheckPtrExistence(TheItem);
  408.         SetItemMark(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff,noMark);
  409.     }
  410.  
  411.  
  412. /* change the name of a menu item */
  413. void                                    ChangeItemName(MenuItemType* TheItem, char* NewName)
  414.     {
  415.         unsigned char                NameTemp[256];
  416.         long                                Scan;
  417.  
  418.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  419.         CheckPtrExistence(TheItem);
  420.         Scan = 0;
  421.         while ((Scan < 255) && (NewName[Scan] != 0))
  422.             {
  423.                 NameTemp[Scan + 1] = NewName[Scan];
  424.                 Scan += 1;
  425.             }
  426.         NameTemp[0] = Scan;
  427.         SetItem(TheItem->WhatMenu->DaMenuHandle,TheItem->MenuManagerID & 0xffff,NameTemp);
  428.     }
  429.  
  430.  
  431. /* Add an implementation defined "separator" to the end of the menu.  On the */
  432. /* Macintosh, this separator is a grey line. */
  433. void                                    AppendSeparator(MenuType* TheMenu)
  434.     {
  435.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  436.         CheckPtrExistence(TheMenu);
  437.         AppendMenu(TheMenu->DaMenuHandle,"\p(-");
  438.     }
  439.  
  440.  
  441. /* Disable all menu items, remove any checkmarks */
  442. void                                    WipeMenusClean(void)
  443.     {
  444.         long                                Scan;
  445.         long                                Limit;
  446.  
  447.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  448.         Limit = ArrayGetLength(OurItemList);
  449.         for (Scan = 0; Scan < Limit; Scan += 1)
  450.             {
  451.                 MenuItemType*                Item;
  452.  
  453.                 Item = (MenuItemType*)ArrayGetElement(OurItemList,Scan);
  454.                 DisableMenuItem(Item);
  455.                 ClearItemCheckmark(Item);
  456.             }
  457.     }
  458.  
  459.  
  460. /* internal routine for converting the number returned from the Toolbox into */
  461. /* one of our own ID numbers.  if the MMID number is an apple menu item, then */
  462. /* it is handled here and NIL is returned. */
  463. MenuItemType*                    Eep_MMID2ItemID(long MMID)
  464.     {
  465.         ERROR(!Initialized,PRERR(ForceAbort,"Menu manager hasn't been initialized"));
  466.         if ((AppleMenu != NIL) && ((MMID >> 16) == AppleMenu->MenuID)
  467.             && ((MMID & 0xffff) > NumAppleMenuItems))
  468.             {
  469.                 Str255                            DeskAccName;
  470.  
  471.                 GetItem(AppleMenu->DaMenuHandle,MMID & 0xffff,DeskAccName);
  472.                 APRINT(("*OpenDeskAcc %p",DeskAccName));
  473.                 OpenDeskAcc(DeskAccName);
  474.                 HiliteMenu(0);
  475.             }
  476.          else
  477.             {
  478.                 long                                Scan;
  479.                 long                                Limit;
  480.  
  481.                 Limit = ArrayGetLength(OurItemList);
  482.                 for (Scan = 0; Scan < Limit; Scan += 1)
  483.                     {
  484.                         MenuItemType*                Item;
  485.  
  486.                         Item = (MenuItemType*)ArrayGetElement(OurItemList,Scan);
  487.                         if (Item->MenuManagerID == MMID)
  488.                             {
  489.                                 return Item;
  490.                             }
  491.                     }
  492.             }
  493.         /* fall through == no item. */
  494.         return NIL;
  495.     }
  496.  
  497.  
  498. /* this is called from the event loop to redraw the menu bar if it needs it. */
  499. /* redraws are deferred and unified to save time. */
  500. void                                    Eep_RedrawMenuBar(void)
  501.     {
  502.         if (RedrawMenuBarFlag)
  503.             {
  504.                 DrawMenuBar();
  505.                 RedrawMenuBarFlag = False;
  506.             }
  507.     }
  508.